// 版权所有2011 Go作者。版权所有。
// 此源代码的使用受BSD样式的约束
// 可以在许可证文件中找到的许可证。

package x509

import (
	"bytes"
	"crypto/sha256"
	"encoding/pem"
	"errors"
	"runtime"
	"sync"
)

type sum224 [sha256.Size224]byte

// CertPool是一组证书。
type CertPool struct {
	byName map[string][]int // cert.RawSubject=>索引到lazyCerts

	// lazyCerts包含返回证书的函数，
	// 根据需要延迟解析/解压缩它。
	lazyCerts []lazyCert

	// haveSum将sum224（cert.Raw）映射为true。它只被使用
	// 对于AddCert重复检测，避免CertPool.contains
	// 在AddCert路径中调用（因为contains方法可以
	// 调用getCert，否则会否定惰性getCert带来的节省
	// funcs）。
	haveSum map[sum224]bool
}

// lazyCert是关于证书和用于检索证书的func的最小元数据
// 以正常的扩展*证书形式。
type lazyCert struct {
	// rawSubject是证书。rawSubject值。
	// 它与CertPool.byName密钥相同，但以[]字节为单位
	// 表单使CertPool.Subjects（由crypto/tls使用）可以
	// 分配更少。
	rawSubject []byte

	// getCert返回证书。
	// None
	// 它并不意味着做网络操作或其他任何事情
	// 可能发生故障的地方；func的意思是懒惰
	// 解析/解压缩已知良好的数据。这个
	// 签名中的错误主要用于
	// 程序运行时本地磁盘上存在证书文件的情况
	// “已启动”将在稍后读取之前删除。
	getCert func() (*Certificate, error)
}

// NewCertPool返回一个新的空CertPool。
func NewCertPool() *CertPool {
	return &CertPool{
		byName:  make(map[string][]int),
		haveSum: make(map[sum224]bool),
	}
}

// len返回集合中的证书数。
// nil集是有效的空集。
func (s *CertPool) len() int {
	if s == nil {
		return 0
	}
	return len(s.lazyCerts)
}

// 证书返回证书索引n，单位为s。
func (s *CertPool) cert(n int) (*Certificate, error) {
	return s.lazyCerts[n].getCert()
}

func (s *CertPool) copy() *CertPool {
	p := &CertPool{
		byName:    make(map[string][]int, len(s.byName)),
		lazyCerts: make([]lazyCert, len(s.lazyCerts)),
		haveSum:   make(map[sum224]bool, len(s.haveSum)),
	}
	for k, v := range s.byName {
		indexes := make([]int, len(v))
		copy(indexes, v)
		p.byName[k] = indexes
	}
	for k := range s.haveSum {
		p.haveSum[k] = true
	}
	copy(p.lazyCerts, s.lazyCerts)
	return p
}

// SystemCertPool返回系统证书池的副本。
// None
// 在macOS以外的Unix系统上，环境变量SSL_CERT_FILE和
// SSL_CERT_DIR可用于覆盖SSL的系统默认位置
// 证书文件和SSL证书文件目录。这个
// 后者可以是以冒号分隔的列表。
// None
// 返回池的任何突变都不会写入磁盘，也不会影响
// SystemCertPool返回的任何其他池。
// None
// 系统证书池中的新更改可能不会反映在后续调用中。
func SystemCertPool() (*CertPool, error) {
	if runtime.GOOS == "windows" {
		// 第1673618609期：
		return nil, errors.New("crypto/x509: system root pool is not available on Windows")
	}

	if sysRoots := systemRootsPool(); sysRoots != nil {
		return sysRoots.copy(), nil
	}

	return loadSystemRoots()
}

// findPotentialParents返回证书的索引，这些索引可能
// 已签署证书。
func (s *CertPool) findPotentialParents(cert *Certificate) []*Certificate {
	if s == nil {
		return nil
	}

	// 考虑所有发行人与证书颁发者匹配的主题。
	// 在挑选可能的候选人时，列表按顺序构建
	// 在构建链中保存周期的匹配合理性：
	// 阿基德打滑比赛
	// 存在AKID，撬块缺失/AKID缺失，撬块存在
	// 阿基德和斯基德不匹配
	var matchingKeyID, oneKeyID, mismatchKeyID []*Certificate
	for _, c := range s.byName[string(cert.RawIssuer)] {
		candidate, err := s.cert(c)
		if err != nil {
			continue
		}
		kidMatch := bytes.Equal(candidate.SubjectKeyId, cert.AuthorityKeyId)
		switch {
		case kidMatch:
			matchingKeyID = append(matchingKeyID, candidate)
		case (len(candidate.SubjectKeyId) == 0 && len(cert.AuthorityKeyId) > 0) ||
			(len(candidate.SubjectKeyId) > 0 && len(cert.AuthorityKeyId) == 0):
			oneKeyID = append(oneKeyID, candidate)
		default:
			mismatchKeyID = append(mismatchKeyID, candidate)
		}
	}

	found := len(matchingKeyID) + len(oneKeyID) + len(mismatchKeyID)
	if found == 0 {
		return nil
	}
	candidates := make([]*Certificate, 0, found)
	candidates = append(candidates, matchingKeyID...)
	candidates = append(candidates, oneKeyID...)
	candidates = append(candidates, mismatchKeyID...)
	return candidates
}

func (s *CertPool) contains(cert *Certificate) bool {
	if s == nil {
		return false
	}
	return s.haveSum[sha256.Sum224(cert.Raw)]
}

// AddCert将证书添加到池中。
func (s *CertPool) AddCert(cert *Certificate) {
	if cert == nil {
		panic("adding nil Certificate to CertPool")
	}
	s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
		return cert, nil
	})
}

// addCertFunc将有关证书的元数据添加到池中，以及
// 稍后需要时获取该证书的func。
// None
// rawSubject为Certificate.rawSubject，必须为非空。
// 可以调用getCert func 0次或多次。
func (s *CertPool) addCertFunc(rawSum224 sum224, rawSubject string, getCert func() (*Certificate, error)) {
	if getCert == nil {
		panic("getCert can't be nil")
	}

	// 检查证书是否未添加两次。
	if s.haveSum[rawSum224] {
		return
	}

	s.haveSum[rawSum224] = true
	s.lazyCerts = append(s.lazyCerts, lazyCert{
		rawSubject: []byte(rawSubject),
		getCert:    getCert,
	})
	s.byName[rawSubject] = append(s.byName[rawSubject], len(s.lazyCerts)-1)
}

// AppendCertsFromPEM尝试解析一系列PEM编码的证书。
// 它会将找到的任何证书附加到，并报告是否存在任何证书
// 已成功解析。
// None
// 在许多Linux系统上，/etc/ssl/cert.pem将包含系统范围的集合
// 以适合此功能的格式创建根CA。
func (s *CertPool) AppendCertsFromPEM(pemCerts []byte) (ok bool) {
	for len(pemCerts) > 0 {
		var block *pem.Block
		block, pemCerts = pem.Decode(pemCerts)
		if block == nil {
			break
		}
		if block.Type != "CERTIFICATE" || len(block.Headers) != 0 {
			continue
		}

		certBytes := block.Bytes
		cert, err := ParseCertificate(certBytes)
		if err != nil {
			continue
		}
		var lazyCert struct {
			sync.Once
			v *Certificate
		}
		s.addCertFunc(sha256.Sum224(cert.Raw), string(cert.RawSubject), func() (*Certificate, error) {
			lazyCert.Do(func() {
				// 这不会失败，因为上面已经分析了相同的字节。
				lazyCert.v, _ = ParseCertificate(certBytes)
				certBytes = nil
			})
			return lazyCert.v, nil
		})
		ok = true
	}

	return ok
}

// Subjects返回的是DER编码的主题列表
// 池中的所有证书。
func (s *CertPool) Subjects() [][]byte {
	res := make([][]byte, s.len())
	for i, lc := range s.lazyCerts {
		res[i] = lc.rawSubject
	}
	return res
}
